import { FairyEditor, System, FairyGUI } from 'csharp';
import CodeWriter from './CodeWriter';
import { $ref } from 'puerts';

export interface Dict<T> {
    [x: number | string]: T;
}

const File = System.IO.File;
const Directory = System.IO.Directory;

enum WindowType {
    None = "None",
    Main = "Main",
    Common = "Common"
}

function SpawnCode(handler: FairyEditor.PublishHandler) {
    let settings = (<FairyEditor.GlobalPublishSettings>handler.project.GetSettings("Publish")).codeGeneration;

    GenClassCode(settings, handler);
    GenEntityCode(settings, handler);
    GenSystemCode(settings, handler);
    GenEventHandlerCode(settings, handler);
    GenFUIBinder(settings, handler);
    GenPanelId(settings, handler);
}

function GetRemark(xmlPath: string) {
    let remark = "";
    let xml = new FairyGUI.Utils.XML(File.ReadAllText(xmlPath));
    if (xml.attributes.TryGetValue("remark", $ref(remark))) {
        remark = xml.attributes.get_Item("remark");
    }
    return remark;
}

function GenFUIBinder(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let autoGenPath = "FUIAutoGen";
    let originExportCodePath = handler.exportCodePath;
    let FUIBinderPath = `${originExportCodePath}\\${autoGenPath}\\FUIBinder.ts`;

    let binderContent = File.ReadAllLines(FUIBinderPath);

    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });

    let filterLineContentList = [
        ``,
        `/** This is an automatically generated class by FairyGUI. Please do not modify it. **/`,
        `export class FUIBinder {`,
        `}`,
        `public static BindAll(): void {`,
        `}`,
    ];
    let importExist = false;
    let bindExist = false;
    let statementImportContent = `import ${codePkgName}Binder from "./${codePkgName}/${codePkgName}Binder";`;
    let statementBindContent = `${codePkgName}Binder.bindAll();`;
    let importContentList: string[] = [];
    let bindContentList: string[] = [];
    for (let i = 0; i < binderContent.Length; i++) {
        let tempContent = binderContent.GetValue(i);
        let lineContent = (<string>tempContent).trim();
        if (filterLineContentList.indexOf(lineContent) != -1) {
            continue;
        }
        let statementContent = lineContent;
        if (statementContent == statementImportContent) {
            importExist = true;
        }
        if (statementContent == statementBindContent) {
            bindExist = true;
        }
        if (statementContent.startsWith(`import`)) {
            importContentList.push(lineContent);
        } else {
            bindContentList.push(lineContent);
        }
    }

    for (let i = 0; i < importContentList.length; i++) {
        let statementContent = importContentList[i];
        writer.writeln(statementContent);
    }
    if (!importExist) {
        writer.writeln(statementImportContent);
    }
    writer.writeln();

    writer.writeln(`export class FUIBinder`);
    writer.startBlock();
    writer.writeln();

    writer.writeln(`public static BindAll(): void`);
    writer.startBlock();
    for (let i = 0; i < bindContentList.length; i++) {
        let statementContent = bindContentList[i];
        writer.writeln(statementContent);
    }
    if (!bindExist) {
        writer.writeln(statementBindContent);
    }
    writer.endBlock();

    writer.writeln();

    writer.endBlock();

    writer.save(FUIBinderPath);
}

function GenPanelId(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let autoGenPath = "FUIAutoGen";
    let originExportCodePath = handler.exportCodePath;
    let PanelIdPath = `${originExportCodePath}\\${autoGenPath}\\PanelId.ts`;

    let panelIdContent = File.ReadAllLines(PanelIdPath);

    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });

    let filterLineContentList = [
        ``,
        `/** This is an automatically generated class by FairyGUI. Please do not modify it. **/`,
        `export enum PanelId {`,
        `}`,
    ];
    let idExist = false;
    let idContentList: string[] = [];
    for (let i = 0; i < panelIdContent.Length; i++) {
        let tempContent = panelIdContent.GetValue(i);
        let lineContent = (<string>tempContent).trim();
        if (filterLineContentList.indexOf(lineContent) != -1) {
            continue;
        }
        let idContent = lineContent;
        if (lineContent.lastIndexOf(",") != -1) {
            idContent = lineContent.replace(",", "").trim();
        }
        if (idContent == codePkgName) {
            idExist = true;
        }
        idContentList.push(lineContent);
    }
    writer.writeln(`export enum PanelId`);
    writer.startBlock();

    for (let i = 0; i < idContentList.length; i++) {
        let idContent = idContentList[i];
        writer.writeln(idContent);
    }
    if (!idExist) {
        writer.writeln(`${codePkgName},`);
    }

    writer.endBlock();

    writer.save(PanelIdPath);
}

function GenEventHandlerCode(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let originExportCodePath = handler.exportCodePath;
    let systemExportCodePath = originExportCodePath.replace("ModelView", "HotfixView");
    let autoGenPath = "FUI";
    let exportCodePath = `${systemExportCodePath}\\${autoGenPath}\\${codePkgName}\\Event`;
    let namespaceName = codePkgName;
    let ns = "fgui";

    if (settings.packageName)
        namespaceName = settings.packageName + '.' + namespaceName;

    if (Directory.Exists(exportCodePath)) {
        return;
    }
    // handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let originClassNamePrefix = settings.classNamePrefix;
    settings.classNamePrefix = "";

    let classes = handler.CollectClasses(true, false, ns);
    handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let basePath = FairyEditor.App.project.basePath;
    let pkgPath = `${basePath}\\assets\\${codePkgName}`;

    let ignoreReg = /^title$|^button$|^bar$|^icon$|^n\d+/i;
    let classCnt = classes.Count;
    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        let xmlPath = `${pkgPath}/${classInfo.res.path}/${classInfo.res.fileName}`;

        if (!File.Exists(xmlPath)) {
            continue;
        }

        let xml = new FairyGUI.Utils.XML(File.ReadAllText(xmlPath));
        let remark = "";
        if (!xml.attributes.TryGetValue("remark", $ref(remark))) {
            continue;
        }
        remark = xml.attributes.get_Item("remark");
        if (remark != WindowType.Main) {
            continue;
        }

        let className = classInfo.className;
        let members = classInfo.members;

        let filePath = `${exportCodePath}/${className}EventHandler.ts`;
        if (File.Exists(filePath)) {
            continue;
        }

        let refPathList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                memberVarType = originClassNamePrefix + memberResName;

                let memberXmlPath = `${memberInfo?.res?.owner?.basePath}/${memberInfo?.res?.path}/${memberInfo?.res?.fileName}`;
                if (!File.Exists(memberXmlPath)) {
                    continue;
                }

                let memberRemark = GetRemark(memberXmlPath);
                if (memberRemark != WindowType.Common) {
                    continue;
                }

                ignoreRef = false;
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refTypeList.push(memberVarType);
        }

        writer.reset();

        writer.writeln(`import { Entity } from "../../../../../../../Core/Module/Entity/Entity";`);
        writer.writeln(`import { ${className} } from "../../../../../../ModelView/Client/Demo/FUI/${codePkgName}/${className}";`);
        writer.writeln(`import { PanelId } from "../../../../../../ModelView/Client/Demo/FUIAutoGen/PanelId";`);
        writer.writeln(`import { FUIEntity } from "../../../../../../ModelView/Client/Plugins/FairyGUI/FUIEntity";`);
        writer.writeln(`import { FUIEvent } from "../../../../../../ModelView/Client/Plugins/FairyGUI/FUIEventAttribute";`);
        writer.writeln(`import { IFUIEventHandler } from "../../../../../../ModelView/Client/Plugins/FairyGUI/IFUIEventHandler";`);
        writer.writeln(`import { UIPanelType } from "../../../../../../ModelView/Client/Plugins/FairyGUI/PanelDataDefine";`);
        writer.writeln();
        writer.writeln(`@FUIEvent(PanelId.${codePkgName}, "${codePkgName}", "${className}")`);
        writer.writeln(`export class ${className}EventHandler implements IFUIEventHandler`);
        writer.startBlock();
        writer.writeln();

        writer.writeln(`OnInitPanelCoreData(fuiEntity: FUIEntity): void`);
        writer.startBlock();
        writer.writeln(`fuiEntity.PanelCoreData.panelType = UIPanelType.Normal;`);
        writer.endBlock();
        writer.writeln();

        writer.writeln(`OnInitComponent(fuiEntity: FUIEntity): void`);
        writer.startBlock();
        writer.writeln(`const panel = fuiEntity.AddComponent<${className}>(${className});`);
        writer.writeln(`panel.Awake();`);
        writer.endBlock();
        writer.writeln();

        writer.writeln(`OnRegisterUIEvent(fuiEntity: FUIEntity): void`);
        writer.startBlock();
        writer.writeln(`fuiEntity.GetComponent<${className}>(${className}).RegisterUIEvent();`);
        writer.endBlock();
        writer.writeln();

        writer.writeln(`OnShow(fuiEntity: FUIEntity, contextData?: Entity): void`);
        writer.startBlock();
        writer.writeln(`fuiEntity.GetComponent<${className}>(${className}).OnShow(contextData);`);
        writer.endBlock();
        writer.writeln();

        writer.writeln(`OnHide(fuiEntity: FUIEntity): void`);
        writer.startBlock();
        writer.writeln(`fuiEntity.GetComponent<${className}>(${className}).OnHide();`);
        writer.endBlock();
        writer.writeln();

        writer.writeln(`BeforeUnload(fuiEntity: FUIEntity): void`);
        writer.startBlock();
        writer.writeln(`fuiEntity.GetComponent<${className}>(${className}).BeforeUnload();`);
        writer.endBlock();
        writer.writeln();

        writer.endBlock();

        writer.save(filePath);
    }
}

function GenSystemCode(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let originExportCodePath = handler.exportCodePath;
    let systemExportCodePath = originExportCodePath.replace("ModelView", "HotfixView");
    let autoGenPath = "FUI";
    let exportCodePath = `${systemExportCodePath}\\${autoGenPath}\\${codePkgName}`;
    let namespaceName = codePkgName;
    let ns = "fgui";

    if (settings.packageName)
        namespaceName = settings.packageName + '.' + namespaceName;

    if (Directory.Exists(exportCodePath)) {
        return;
    }
    // handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let originClassNamePrefix = settings.classNamePrefix;
    settings.classNamePrefix = "";

    let classes = handler.CollectClasses(true, false, ns);
    handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let basePath = FairyEditor.App.project.basePath;
    let pkgPath = `${basePath}\\assets\\${codePkgName}`;

    let ignoreReg = /^title$|^button$|^bar$|^icon$|^n\d+/i;
    let classCnt = classes.Count;
    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);

        let xmlPath = `${pkgPath}/${classInfo.res.path}/${classInfo.res.fileName}`;
        if (!File.Exists(xmlPath)) {
            continue;
        }

        let remark = GetRemark(xmlPath);
        if (remark != WindowType.Common) {
            continue;
        }

        let className = classInfo.className;
        let members = classInfo.members;

        let filePath = `${exportCodePath}/${className}System.ts`;
        if (File.Exists(filePath)) {
            continue;
        }

        let refPathList: string[] = [];
        let refNameList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                memberVarType = originClassNamePrefix + memberResName;

                let memberXmlPath = `${memberInfo?.res?.owner?.basePath}/${memberInfo?.res?.path}/${memberInfo?.res?.fileName}`;
                if (!File.Exists(memberXmlPath)) {
                    continue;
                }

                let memberRemark = GetRemark(memberXmlPath);
                if (memberRemark != WindowType.Common) {
                    continue;
                }

                ignoreRef = false;
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refNameList.push(memberVarName);
            refTypeList.push(memberVarType);
        }

        let exportComponentName = `FUI_${className}`;
        let refCount = refPathList.length;

        writer.reset();

        writer.writeln(`import { Entity } from "../../../../../../Core/Module/Entity/Entity";`);
        writer.writeln(`import { Awake1System } from "../../../../../../Core/Module/EventSystem/IAwakeSystem";`);
        writer.writeln(`import { ExpandOf, FriendOf, ObjectSystemAttribute } from "../../../../../../Decorator/Decorator";`);
        writer.writeln(`import { ${className} } from "../../../../../ModelView/Client/Demo/FUI/${codePkgName}/${className}";`);

        for (let i = 0; i < refCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import { ${refType} } from "../../../../../ModelView/Client/Demo/FUI/${refPath}/${refType}";`);
        }

        writer.writeln(`import ${exportComponentName} from "../../../../../ModelView/Client/Demo/FUIAutoGen/${codePkgName}/${exportComponentName}";`);

        for (let i = 0; i < refCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import FUI_${refType} from "../../../../../ModelView/Client/Demo/FUIAutoGen/${refPath}/FUI_${refType}";`);
        }

        writer.writeln();
        writer.writeln(`/**`);
        writer.writeln(` * 使用 Expand 扩展方法`);
        writer.writeln(` * `);
        writer.writeln(` * 必须作出以下申明，才能调用对象的扩展方法`);
        writer.writeln(` */`);
        writer.writeln(`declare module "../../../../../ModelView/Client/Demo/FUI/${codePkgName}/${className}"`);
        writer.startBlock();
        writer.writeln();
        writer.writeln(`interface ${className}`);
        writer.startBlock();
        writer.writeln(`Awake(fui${className}: ${exportComponentName}): void;`);
        writer.writeln(`RegisterUIEvent(): void;`);
        writer.writeln(`OnShow(contextData?: Entity): void;`);
        writer.writeln(`OnHide(): void;`);
        writer.writeln(`BeforeUnload(): void;`);
        writer.endBlock();
        writer.writeln();
        writer.endBlock();
        writer.writeln();
        writer.writeln(`@ExpandOf(${className})`);
        writer.writeln(`export class ${className}System`);
        writer.startBlock();
        writer.writeln();

        writer.writeln(`private static Awake(self: ${className}, fui${className}: ${exportComponentName}): void`);
        writer.startBlock();
        writer.writeln(`self.FUI${className} = fui${className};`);
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refName = refNameList[i];
                let refType = refTypeList[i];
                writer.writeln(`self.${refType} = self.AddChild<${refType}, FUI_${refType}>(${refType}, self.FUI${className}.${refName});`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static RegisterUIEvent(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.RegisterUIEvent();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static OnShow(self: ${className}, contextData: Entity = null): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.OnShow();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static OnHide(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.OnHide();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static BeforeUnload(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.BeforeUnload();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.endBlock();

        writer.writeln();
        writer.writeln(`@FriendOf(${className})`);
        writer.writeln(`@ObjectSystemAttribute()`);
        writer.writeln(`export class ${className}Awake1System extends Awake1System<${className}, ${exportComponentName}>`);
        writer.startBlock();
        writer.writeln();
        writer.writeln(`Awake(self: ${className}, fui${className}: ${exportComponentName}): void`);
        writer.startBlock();
        writer.writeln(`self.Awake(fui${className});`);
        writer.endBlock();
        writer.writeln();
        writer.endBlock();

        writer.save(filePath);
    }
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);

        let xmlPath = `${pkgPath}/${classInfo.res.path}/${classInfo.res.fileName}`;
        if (!File.Exists(xmlPath)) {
            continue;
        }

        let xml = new FairyGUI.Utils.XML(File.ReadAllText(xmlPath));
        let remark = "";
        if (!xml.attributes.TryGetValue("remark", $ref(remark))) {
            continue;
        }
        remark = xml.attributes.get_Item("remark");
        if (remark != WindowType.Main) {
            continue;
        }

        let className = classInfo.className;
        let members = classInfo.members;

        let filePath = `${exportCodePath}/${className}System.ts`;
        if (File.Exists(filePath)) {
            continue;
        }

        let refPathList: string[] = [];
        let refNameList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                memberVarType = originClassNamePrefix + memberResName;

                let memberXmlPath = `${memberInfo?.res?.owner?.basePath}/${memberInfo?.res?.path}/${memberInfo?.res?.fileName}`;
                if (!File.Exists(memberXmlPath)) {
                    continue;
                }

                let memberRemark = GetRemark(memberXmlPath);
                if (memberRemark != WindowType.Common) {
                    continue;
                }

                ignoreRef = false;
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refNameList.push(memberVarName);
            refTypeList.push(memberVarType);
        }

        let refCount = refPathList.length;

        writer.reset();

        writer.writeln(`import { Entity } from "../../../../../../Core/Module/Entity/Entity";`);
        writer.writeln(`import { ExpandOf } from "../../../../../../Decorator/Decorator";`);

        for (let i = 0; i < refCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import { ${refType} } from "../../../../../ModelView/Client/Demo/FUI/${refPath}/${refType}";`);
        }

        writer.writeln(`import { ${className} } from "../../../../../ModelView/Client/Demo/FUI/${codePkgName}/${className}";`);

        for (let i = 0; i < refCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import FUI_${refType} from "../../../../../ModelView/Client/Demo/FUIAutoGen/${refPath}/FUI_${refType}";`);
        }

        writer.writeln();
        writer.writeln(`/**`);
        writer.writeln(` * 使用 Expand 扩展方法`);
        writer.writeln(` * `);
        writer.writeln(` * 必须作出以下申明，才能调用对象的扩展方法`);
        writer.writeln(` */`);
        writer.writeln(`declare module "../../../../../ModelView/Client/Demo/FUI/${codePkgName}/${className}"`);
        writer.startBlock();
        writer.writeln();
        writer.writeln(`interface ${className}`);
        writer.startBlock();
        writer.writeln(`Awake(): void;`);
        writer.writeln(`RegisterUIEvent(): void;`);
        writer.writeln(`OnShow(contextData: Entity): void;`);
        writer.writeln(`OnHide(): void;`);
        writer.writeln(`BeforeUnload(): void;`);
        writer.endBlock();
        writer.writeln();
        writer.endBlock();
        writer.writeln();
        writer.writeln(`@ExpandOf(${className})`);
        writer.writeln(`export class ${className}System`);
        writer.startBlock();
        writer.writeln();

        writer.writeln(`private static Awake(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refName = refNameList[i];
                let refType = refTypeList[i];
                writer.writeln(`self.${refType} = self.AddChild<${refType}, FUI_${refType}>(${refType}, self.FUI${className}.${refName});`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static RegisterUIEvent(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.RegisterUIEvent();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static OnShow(self: ${className}, contextData: Entity = null): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.OnShow();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static OnHide(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.OnHide();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.writeln(`private static BeforeUnload(self: ${className}): void`);
        writer.startBlock();
        if (refCount > 0) {
            for (let i = 0; i < refCount; i++) {
                let refType = refTypeList[i];
                writer.writeln(`self.${refType}.BeforeUnload();`);
            }
        } else {
            writer.writeln(``);
        }
        writer.endBlock();
        writer.writeln();

        writer.endBlock();

        writer.save(filePath);
    }

    settings.classNamePrefix = originClassNamePrefix;
}

function GenEntityCode(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let autoGenPath = "FUI";
    let exportCodePath = `${handler.exportCodePath}\\${autoGenPath}\\${codePkgName}`;
    let namespaceName = codePkgName;
    let ns = "fgui";

    if (settings.packageName)
        namespaceName = settings.packageName + '.' + namespaceName;

    let originClassNamePrefix = settings.classNamePrefix;
    settings.classNamePrefix = "";

    let classes = handler.CollectClasses(true, false, ns);
    handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let basePath = FairyEditor.App.project.basePath;
    let pkgPath = `${basePath}\\assets\\${codePkgName}`;

    let ignoreReg = /^title$|^button$|^bar$|^icon$|^n\d+/i;
    let classCnt = classes.Count;
    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);

        let xmlPath = `${pkgPath}/${classInfo.res.path}/${classInfo.res.fileName}`;
        if (!File.Exists(xmlPath)) {
            continue;
        }

        let remark = GetRemark(xmlPath);
        if (remark != WindowType.Common) {
            continue;
        }

        let className = classInfo.className;
        let members = classInfo.members;

        let refPathList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                memberVarType = originClassNamePrefix + memberResName;

                let memberXmlPath = `${memberInfo?.res?.owner?.basePath}/${memberInfo?.res?.path}/${memberInfo?.res?.fileName}`;
                if (!File.Exists(memberXmlPath)) {
                    continue;
                }

                let memberRemark = GetRemark(memberXmlPath);
                if (memberRemark != WindowType.Common) {
                    continue;
                }

                ignoreRef = false;
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refTypeList.push(memberVarType);
        }

        let exportComponentName = `FUI_${className}`;

        writer.reset();

        writer.reset();
        writer.writeln(`import { Entity } from "../../../../../../Core/Module/Entity/Entity";`);
        writer.writeln(`import { IAwake1 } from "../../../../../../Core/Module/EventSystem/IAwakeSystem";`);
        writer.writeln(`import ${exportComponentName} from "../../FUIAutoGen/${codePkgName}/${exportComponentName}";`);

        let refPathCount = refPathList.length;
        for (let i = 0; i < refPathCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import { ${refType} } from "../${refPath}/${refType}";`);
        }

        writer.writeln();
        writer.writeln(`export class ${className} extends Entity implements IAwake1<${exportComponentName}>`);
        writer.startBlock();
        writer.writeln();

        for (let i = 0; i < refPathCount; i++) {
            let refType = refTypeList[i];
            writer.writeln('public %s: %s;', refType, refType);
            writer.writeln();
        }
        writer.writeln('public %s: %s;', `FUI${className}`, `${exportComponentName}`);

        writer.writeln();
        writer.endBlock();

        writer.save(`${exportCodePath}/${className}.ts`);
    }
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        let xmlPath = `${pkgPath}/${classInfo.res.path}/${classInfo.res.fileName}`;

        if (!File.Exists(xmlPath)) {
            continue;
        }

        let xml = new FairyGUI.Utils.XML(File.ReadAllText(xmlPath));
        let remark = "";
        if (!xml.attributes.TryGetValue("remark", $ref(remark))) {
            continue;
        }
        remark = xml.attributes.get_Item("remark");
        if (remark != WindowType.Main) {
            continue;
        }

        let className = classInfo.className;
        let members = classInfo.members;

        let refPathList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                memberVarType = originClassNamePrefix + memberResName;

                let memberXmlPath = `${memberInfo?.res?.owner?.basePath}/${memberInfo?.res?.path}/${memberInfo?.res?.fileName}`;
                if (!File.Exists(memberXmlPath)) {
                    continue;
                }

                let memberRemark = GetRemark(memberXmlPath);
                if (memberRemark != WindowType.Common) {
                    continue;
                }

                ignoreRef = false;
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refTypeList.push(memberVarType);
        }

        let exportComponentName = `FUI_${className}`;

        writer.reset();

        writer.reset();
        writer.writeln(`import { Entity } from "../../../../../../Core/Module/Entity/Entity";`);
        writer.writeln(`import { IAwake } from "../../../../../../Core/Module/EventSystem/IAwakeSystem";`);
        writer.writeln(`import { FUIEntity } from "../../../Plugins/FairyGUI/FUIEntity";`);
        writer.writeln(`import ${exportComponentName} from "../../FUIAutoGen/${codePkgName}/${exportComponentName}";`);

        let refPathCount = refPathList.length;
        for (let i = 0; i < refPathCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import { ${refType} } from "../${refPath}/${refType}";`);
        }

        writer.writeln();
        writer.writeln(`export class ${className} extends Entity implements IAwake`);
        writer.startBlock();
        writer.writeln();

        for (let i = 0; i < refPathCount; i++) {
            let refType = refTypeList[i];
            writer.writeln('public %s: %s;', refType, refType);
            writer.writeln();
        }
        writer.writeln(`private _fui${className}: ${exportComponentName};`);
        writer.writeln();
        writer.writeln(`public get FUI${className}(): ${exportComponentName}`);
        writer.startBlock();
        writer.writeln(`if (!this._fui${className})`);
        writer.startBlock();
        writer.writeln(`this._fui${className} = <${exportComponentName}>this.GetParent<FUIEntity>().GComponent;`);
        writer.endBlock();
        writer.writeln(`return this._fui${className};`);
        writer.endBlock();

        writer.writeln();
        writer.endBlock();

        writer.save(`${exportCodePath}/${className}.ts`);
    }

    settings.classNamePrefix = originClassNamePrefix;
}

function GenClassCode(settings: FairyEditor.GlobalPublishSettings.CodeGenerationConfig, handler: FairyEditor.PublishHandler) {
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let autoGenPath = "FUIAutoGen";
    let exportCodePath = `${handler.exportCodePath}\\${autoGenPath}\\${codePkgName}`;
    let namespaceName = codePkgName;
    let ns = "fgui";
    let isThree = handler.project.type == FairyEditor.ProjectType.ThreeJS;

    if (settings.packageName)
        namespaceName = settings.packageName + '.' + namespaceName;

    let originClassNamePrefix = settings.classNamePrefix;
    settings.classNamePrefix = "FUI_";

    //CollectClasses(stripeMemeber, stripeClass, fguiNamespace)
    let classes = handler.CollectClasses(true, false, ns);
    handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files

    let getMemberByName = settings.getMemberByName;

    let ignoreReg = /^title$|^button$|^bar$|^icon$|^n\d+/i;
    let classCnt = classes.Count;
    let writer = new CodeWriter({ blockFromNewLine: false, usingTabs: true });
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        let members = classInfo.members;
        let references = classInfo.references;

        let refPathList: string[] = [];
        let refTypeList: string[] = [];
        let memberCnt = members.Count;
        for (let j: number = 0; j < memberCnt; j++) {
            let memberInfo = members.get_Item(j);

            let ignoreRef = true;
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                if (memberPkgName != codePkgName) {
                    memberVarType = originClassNamePrefix + memberResName;
                    ignoreRef = false;
                }
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (ignoreRef) {
                continue;
            }
            refPathList.push(memberPkgName);
            refTypeList.push(memberVarType);
        }

        writer.reset();

        let refPathCount = refPathList.length;
        for (let i = 0; i < refPathCount; i++) {
            let refPath = refPathList[i];
            let refType = refTypeList[i];
            writer.writeln(`import ${refType} from "../${refPath}/FUI_${refType}";`);
        }
        let refCount = references.Count;
        if (refCount > 0) {
            for (let j: number = 0; j < refCount; j++) {
                let ref = references.get_Item(j);
                writer.writeln('import %s from "./%s";', ref, ref);
            }
        }
        if (isThree) {
            writer.writeln('import * as fgui from "fairygui-three";');
        }
        if (refPathCount > 0 || refCount > 0 || isThree)
            writer.writeln();

        writer.writeln('export default class %s extends %s', classInfo.className, classInfo.superClassName);
        writer.startBlock();
        writer.writeln();

        let validMemberCount = 0;
        for (let i: number = 0; i < memberCnt; i++) {
            let memberInfo = members.get_Item(i);

            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                if (memberPkgName != codePkgName) {
                    memberVarType = originClassNamePrefix + memberResName;
                }
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            writer.writeln('public %s: %s;', memberVarName, memberVarType);
            validMemberCount++;
        }
        if (validMemberCount > 0)
            writer.writeln();

        writer.writeln('public static URL: string = "ui://%s%s";', handler.pkg.id, classInfo.resId);
        writer.writeln();

        writer.writeln('public static createInstance(): %s', classInfo.className);
        writer.startBlock();
        writer.writeln('return <%s>(%s.UIPackage.createObject("%s", "%s"));', classInfo.className, ns, handler.pkg.name, classInfo.resName);
        writer.endBlock();
        writer.writeln();

        writer.writeln('protected onConstruct(): void');
        writer.startBlock();
        for (let i: number = 0; i < memberCnt; i++) {
            let memberInfo = members.get_Item(i);
            let memberVarName = memberInfo?.varName;
            let memberVarType = memberInfo?.type;
            let memberResName = memberInfo?.res?.name;
            let memberPkgName = memberInfo?.res?.owner?.name;
            if (memberResName && memberPkgName) {
                if (memberPkgName != codePkgName) {
                    memberVarType = originClassNamePrefix + memberResName;
                }
            }
            let ignoreReuslt = ignoreReg.test(memberVarName);
            if (ignoreReuslt) {
                continue;
            }
            if (memberInfo.group == 0) {
                if (getMemberByName)
                    writer.writeln('this.%s = <%s>(this.getChild("%s"));', memberInfo.varName, memberVarType, memberInfo.name);
                else
                    writer.writeln('this.%s = <%s>(this.getChildAt(%s));', memberInfo.varName, memberVarType, memberInfo.index);
            }
            else if (memberInfo.group == 1) {
                if (getMemberByName)
                    writer.writeln('this.%s = this.getController("%s");', memberInfo.varName, memberInfo.name);
                else
                    writer.writeln('this.%s = this.getControllerAt(%s);', memberInfo.varName, memberInfo.index);
            }
            else {
                if (getMemberByName)
                    writer.writeln('this.%s = this.getTransition("%s");', memberInfo.varName, memberInfo.name);
                else
                    writer.writeln('this.%s = this.getTransitionAt(%s);', memberInfo.varName, memberInfo.index);
            }
        }
        writer.endBlock();

        writer.writeln();

        writer.endBlock();

        writer.save(`${exportCodePath}/${classInfo.className}.ts`);
    }

    writer.reset();

    let binderName = codePkgName + 'Binder';

    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        writer.writeln('import %s from "./%s";', classInfo.className, classInfo.className);
    }

    if (isThree) {
        writer.writeln('import * as fgui from "fairygui-three";');
        writer.writeln();
    }

    writer.writeln();
    writer.writeln('export default class %s', binderName);
    writer.startBlock();
    writer.writeln();

    writer.writeln('public static bindAll(): void');
    writer.startBlock();
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        writer.writeln('%s.UIObjectFactory.setExtension(%s.URL, %s);', ns, classInfo.className, classInfo.className);
    }
    writer.endBlock(); //bindall

    writer.writeln();
    writer.endBlock(); //class

    writer.save(`${exportCodePath}/${binderName}.ts`);

    settings.classNamePrefix = originClassNamePrefix;
}

export { SpawnCode };